﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Text.RegularExpressions;
using System.Diagnostics;
using CustomizedParamList = System.Collections.Generic.Dictionary<string, string>;


namespace dmm
{
    public enum TaskState
    {
        TaskState_Wait,
        TaskState_Active,
        TaskState_Paused,
        TaskState_Done,
        TaskState_Canceled,
        TaskState_Error,
    };

    public class Task:INotifyPropertyChanged
    {
        
        public int target_width
        {
            get;
            set;
        }
        public int target_height
        {
            get;
            set;
        }

        public int error = 0;
        public string error_string = "";
        public double progress = 0;

        public Video video = null;
        public Profile profile = null;
        public String target_file = null;
        public TaskState state = TaskState.TaskState_Wait;
        public CustomizedParamList user_param = null;

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

       
        public double TargetDuration
        {
            get
            {
                if (video.is_trimed())
                    return (video.VideoSetting.trim_end - video.VideoSetting.trim_start);
                else
                    return video.Duration;
            }
        }
        
        public string ProgressString
        {
            get
            { 
                return String.Format(@"{0}%",progress);
            }
        }
        public double Progress
        {
            get { return progress; }
            set { 
                progress = value;
                OnPropertyChanged("Progress");
                OnPropertyChanged("ProgressString");
            }
        }
        public Video TheVideo{
            get { return video; }
        }
        public Profile TheProfile
        {
            get { return profile; }
        }
        public TaskState State
        {
            set
            {
                state = value;
                OnPropertyChanged("StateString");
            }
            get
            {
                return state;
            }

        }
        public bool CanStart
        {
            get
            {
                return (State != TaskState.TaskState_Active && State != TaskState.TaskState_Done);
            }
        }
        public bool CanStop
        {
            get
            {
                return (State == TaskState.TaskState_Active || State == TaskState.TaskState_Wait || State == TaskState.TaskState_Paused);
            }
        }
        public String StateString
        {
          
            get
            {
                string sstr = "";
                switch (state)
                {
                    case TaskState.TaskState_Active:
                        sstr = "active";
                        break;
                    case TaskState.TaskState_Canceled:
                        sstr = "canceled";
                        break;
                    case TaskState.TaskState_Done:
                        sstr = "finished";
                        break;
                    case TaskState.TaskState_Error:
                        sstr = "failed";
                        break;
                    case TaskState.TaskState_Paused:
                        sstr = "paused";
                        break;
                    case TaskState.TaskState_Wait:
                        sstr = "ready";
                        break;
                }
                return sstr;
            }
        }
        public int Error
        {
            set
            {
                error = value;
                OnPropertyChanged("Remark");
            }
        }
        public string Remark
        {
            get
            {
                switch (error)
                {
                    case 0:
                        return "";
                    case -1:
                        return "Task canceled by user!";
                };
                return error_string;
            }
        }
        #region constructor
        public Task(Video v, Profile profile, string target_file)
        {
            this.video = v;
            this.profile = profile;
            if (target_file == null)
            {
                this.target_file = Options.newUniqueFile(Options.Instance.VideoOutPath, v.FileNameNoExt, profile.FileExt);
            }
            else
                this.target_file = target_file;

            target_width = v.Width;
            target_height = v.Height;

            this.user_param = new CustomizedParamList();
            user_param.Add("output", this.target_file);

        }
         ~Task()
        {
        }

        #endregion

        

         private void replace_param(ref string cmd,  string macro, string val)
        {
             
            if ((macro == "vdimension") && (cmd.Contains("$(width)") ||cmd.Contains("$(height)")))
            {
               
                Match match = Regex.Match(val, @"([0-9]+)x([0-9]+)", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    target_width = int.Parse(match.Groups[1].Value);
                    target_height = int.Parse(match.Groups[2].Value);
                }


                cmd = cmd.Replace("$(width)", target_width.ToString());
                cmd = cmd.Replace("$(height)", target_height.ToString());  
            }
            else if ((macro == "vzoom")  && cmd.Contains("$(zoommode)"))
            {
              
                string strz="";
                int sw = video.Width;
                int sh = video.Height;
                int cx = 0;
                int cy = 0;
               
                if (video.is_cropped())
                {
                    cx = (int)(video.Width * video.VideoSetting.crop_rect.Left);
                    cy = (int)(video.Height * video.VideoSetting.crop_rect.Top);
                    sw = (int)(video.Width * video.VideoSetting.crop_rect.Width);
                    sh = (int)(video.Height * video.VideoSetting.crop_rect.Height);
                    if(val != "PanScan") strz = String.Format("crop={0}:{1}:{2}:{3},", sw, sh, cx, cy);  
                }
                
            
                double dratio = (double)target_width / target_height; 
                double sratio = (double)sw/sh;

                int xw=sw,xh=sh;

                //compute the expanded size
                if (val == "Letterbox")
                {
                      if(dratio > sratio){//increase width
                          xh = sh;
                          xw = (int)(xh *dratio);
                      }else{//increase height
                          xw = sw;
                          xh = (int)(xw/dratio);
                      }
                    strz = strz + String.Format("expand={0}:{1}:::0,", xw, xh);
                }//expand[=w:h:x:y:o:a:r]
                else if (val == "PanScan")
                {
                    if (dratio > sratio)
                    {//decrase height
                        xw = sw;
                        xh = (int)(xw /dratio);
                        cy += (sh - xh) / 2;
                    }
                    else
                    {//decrease width
                        xh = sh;
                        xw = (int)(xh * dratio);
                        cx += (sw - xw) / 2;
                    }
                    strz = strz + String.Format("crop={0}:{1}:{2}:{3},", xw, xh, cx, cy);
                }
                else if (val == "Stretch")
                {
                } 
                cmd = cmd.Replace("$(zoommode)", strz);
            }
            else if(cmd.Contains("$(" + macro + ")"))
            {
                cmd = cmd.Replace("$(" + macro + ")", val);
            }
        }


         public string get_commandline()
         {
             string cmd = String.Copy(profile.cmd);
             cmd = cmd.Replace("$(input)", "\"" + video.Path + "\"");
             cmd = cmd.Replace("$(output)", this.target_file);

             //cancat video custom param and profile custom param
             var cparams = user_param.Concat(profile.user_param);
             if (cparams != null)
             {
                 foreach (KeyValuePair<string, string> kvp in cparams)
                 {
                     string val = kvp.Value;
                     replace_param(ref cmd, kvp.Key, val);
                 }
             }


             if (profile.param_dict != null)
             {
                 foreach (KeyValuePair<string, ProfileParam> kvp in profile.param_dict)
                 {
                     //get default value
                     string default_val = kvp.Value.default_value;
                     
                     //Debug.Assert(default_val != null);
                     if (default_val != null)
                         replace_param(ref cmd, kvp.Key, default_val);
                 }
             }

             if (video.is_trimed())
             {
                 cmd = "-ss " + video.VideoSetting.trim_start + " -endpos " + (video.VideoSetting.trim_end - video.VideoSetting.trim_start) + " -hr-edl-seek " + cmd;
                 //int frame_cnt = (video.trim_end-video.trim_start)
                 // cmd = "-edl edl"+video.rowid+".txt -hr-edl-seek  " + cmd;
                   //-edl edl.txt -hr-edl-seek的问题在于需要从桢0一直遍历到video结尾，速度太慢，不实用
                 //-ss -endpos 的问题在于定位不准确，只能跳到关键桢
                //一种解决，是把edl的使用作为选项，由用户自己决定是否采用

             }

             //TODO:the param not processed yet
             cmd = cmd.Replace("-ofps Original", "-ofps 25");
             cmd = cmd.Replace("$(effect)", "");
             cmd = cmd.Replace("pullup,softskip,", "");
           //  cmd += " -ass -embeddedfonts -sub-fuzziness 1";

             if (video.selected_aid != -1) cmd += string.Format(" -aid {0} ", video.selected_aid);
             if (video.selected_tid != -1) cmd += string.Format(" -sid {0} ", video.selected_tid);

             Trace.Write("-------------------------------------------\n");
             Trace.Write(cmd+"\n");
             Trace.Write("-------------------------------------------\n");
             return cmd;

         }

      

        #region command
        #endregion

        #region event
        public void onStarted()
        {
            State = TaskState.TaskState_Active;
        }

        public void onProgress(double progress)
        {
            this.progress = progress;
        }

        public void onFailed(int err)
        {
            this.State = TaskState.TaskState_Error;
            this.Error = err;
        }

        public void onPaused()
        {
            this.State = TaskState.TaskState_Paused;
        }

        public void onCanceled()
        {
            this.State = TaskState.TaskState_Canceled;
        }

        public void onFinished()
        {
            this.State = TaskState.TaskState_Done;
            this.Progress = 100;
        }
        #endregion
    }
}
